/**
 * $Revision: 1722 $
 * $Date: 2005-07-28 15:19:16 -0700 (Thu, 28 Jul 2005) $
 *
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.kingray.openfire.plugin;

import org.jivesoftware.admin.AuthCheckFilter;
import org.jivesoftware.openfire.IQRouter;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.group.DefaultGroupProvider;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupAlreadyExistsException;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.openfire.group.GroupProvider;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.PropertyEventListener;
import org.jivesoftware.util.PropertyEventDispatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;

import com.kingray.openfire.plugin.handler.iq.EmotionIQHandler;
import com.kingray.openfire.plugin.handler.iq.KingrayMessageHistoryIQHandler;
import com.kingray.openfire.plugin.listener.MessageHistoryListener;
import com.xiongyingqi.openfire.util.SequenceGenerator;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Plugin that allows the administration of users via HTTP requests.
 *
 * @author Justin Hunt
 */
public class KingrayServicePlugin implements Plugin, PropertyEventListener {
    private UserManager userManager;
    private XMPPServer server;
    private String secret;
    private boolean enabled;
    private Collection<String> allowedIPs;
    private GroupProvider groupProvider;
    
    public static final Logger log = LoggerFactory.getLogger(KingrayServicePlugin.class);
    /**
     * 插件基础地址
     */
    public static final String PLUGIN_BASE_REQUEST = "/plugins/kingrayplugin/";
    
    private MessageHistoryListener messageHistoryListener = new MessageHistoryListener();
    
    private void initKingrayService(){
    	new KingrayMessageHistoryIQHandler(); // 启动历史消息处理器
    	new EmotionIQHandler(); // 启动表情处理器
    }
    
    
    @Override
	public void initializePlugin(PluginManager manager, File pluginDirectory) {
    	initKingrayService();
//    	JiveGlobals.setProperty("provider.auth.className", "com.kingray.plugin.auth.CryptAuthProvider"); // 设置验证类
//    	JiveGlobals.setProperty("provider.user.className", "com.kingray.plugin.user.CryptUserProvider"); // 设置用户管理类
//    	JiveGlobals.setProperty("user.usePlainPassword", "false"); // 设置不允许使用明文密码
    	groupProvider = new DefaultGroupProvider();
    	
        server = XMPPServer.getInstance();
        userManager = server.getUserManager();

        secret = JiveGlobals.getProperty("plugin.userservice.secret", "");
        
        // If no secret key has been assigned to the user service yet, assign a random one.
        if (secret.equals("")){
            secret = StringUtils.randomString(8);
            setSecret(secret);
        }
        
        // See if the service is enabled or not.
        enabled = JiveGlobals.getBooleanProperty("plugin.userservice.enabled", false);

        // Get the list of IP addresses that can use this service. An empty list means that this filter is disabled.
        allowedIPs = StringUtils.stringToCollection(JiveGlobals.getProperty("plugin.userservice.allowedIPs", ""));

        // Listen to system property events
        PropertyEventDispatcher.addListener(this);
        // 放行服务页面
        AuthCheckFilter.addExclude("kingrayplugin/pages/*");
        AuthCheckFilter.addExclude("kingrayplugin/js/*");
        AuthCheckFilter.addExclude("kingrayplugin/images/*");
        // 注册消息拦截
        InterceptorManager.getInstance().addInterceptor(messageHistoryListener);
//     	MessageListenerRouter.getInstance().addMessageListener(messageHistoryListener);
    }

    @Override
	public void destroyPlugin() {
        userManager = null;
        // Stop listening to system property events
        PropertyEventDispatcher.removeListener(this);
        
        // 放行服务页面
        AuthCheckFilter.removeExclude("kingrayplugin/pages/*");
        AuthCheckFilter.removeExclude("kingrayplugin/js/*");
        AuthCheckFilter.removeExclude("kingrayplugin/images/*");
        // 取消消息拦截
        InterceptorManager.getInstance().removeInterceptor(messageHistoryListener);
//        MessageListenerRouter.getInstance().removeMessageListener(messageHistoryListener);
    }
    /**
     * 验证权限
     * @return
     */
    public boolean validate(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
    	if (!this.getAllowedIPs().isEmpty()) { // 验证允许ip
			// Get client's IP address
			String ipAddress = request.getHeader("x-forwarded-for");
			if (ipAddress == null) {
				ipAddress = request.getHeader("X_FORWARDED_FOR");
				if (ipAddress == null) {
					ipAddress = request.getHeader("X-Forward-For");
					if (ipAddress == null) {
						ipAddress = request.getRemoteAddr();
					}
				}
			}
			if (!this.getAllowedIPs().contains(ipAddress)) {
				log.warn("User service rejected service to IP address: "
						+ ipAddress);
				replyMessage("RequestNotAuthorised", response);
				return false;
			}
		}
		// Check that our plugin is enabled.
		if (!this.isEnabled()) { // 服务是否关闭
			log.warn("User service plugin is disabled: "
					+ request.getQueryString());
			replyMessage("UserServiceDisabled", response);
			return false;
		}

		// Check this request is authorised
		String secret = request.getParameter("secret");
		if (secret == null || !secret.equals(this.getSecret())) { // 验证密钥
			log.warn("An unauthorised user service request was received: "
					+ request.getQueryString());
			replyMessage("RequestNotAuthorised", response);
			return false;
		}
		return true;
    }
    private void replyMessage(String message, HttpServletResponse response) {
		try {
			PrintWriter out = response.getWriter();
			response.setContentType("text/plain");
			out.print(message);
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
    /**
     * 添加用户，如果分组不存在，则会创建组
     * @param username
     * @param password
     * @param name
     * @param email
     * @param groupNames
     * @throws UserAlreadyExistsException
     */
    public void createUser(String username, String password, String name, String email, String groupNames, int sortNumber)
            throws UserAlreadyExistsException
    {
        userManager.createUser(username, password, name, email);
        if (groupNames != null) {
//            if(groupNames.contains(",")){ // 如果有","号分隔符，则添加多个分组
            	StringTokenizer tkn = new StringTokenizer(groupNames, ",");
            	while (tkn.hasMoreTokens()) {
            		String groupName = tkn.nextToken();
            		log.debug("groupName ============= " + groupName);
            		Group group = getOrCreateGroup(groupName);
            		if(sortNumber < 0){
            			sortNumber = SequenceGenerator.nextInt();
            		}
            		group.getMembers().add(server.createJID(username, null));
            		groupProvider.updateMemberSortNumber(group.getName(), server.createJID(username, null), sortNumber);
            	}
//            } else { // 如果没有分隔符，则添加一个
//            	
//            }
        }
    }
    /**
     * 获取或者添加一个组
     * @param groupName
     * @return 如果该组名存在，则返回该组
     * 			如果组名不存在，则创建并返回该组
     */
    private Group getOrCreateGroup(String groupName){
    	Group group = null;
    	GroupManager groupManager = GroupManager.getInstance();
    	try{
    		group = groupManager.getGroup(groupName);
    	} catch (GroupNotFoundException e) {
    		//忽略
		}
		if(group == null){
			try {
				group = groupManager.createGroup(groupName);
				
				// 设置共享为所有人
				String propValue = "everybody"; 
				group.getProperties().put("sharedRoster.showInRoster", propValue);
				group.getProperties().put("sharedRoster.displayName", groupName);
			} catch (GroupAlreadyExistsException e) {
			}
		}
		
    	return group;
    }
    
    public void deleteUser(String username) throws UserNotFoundException{
        User user = getUser(username);
        userManager.deleteUser(user);
    }

    /**
     * Lock Out on a given username
     *
     * @param username the username of the local user to disable.
     * @throws UserNotFoundException if the requested user
     *         does not exist in the local server.
     */
    public void disableUser(String username) throws UserNotFoundException
    {
        User user = getUser(username);
        LockOutManager.getInstance().disableAccount(username, null, null);
    }

    /**
     * Remove the lockout on a given username
     *
     * @param username the username of the local user to enable.
     * @throws UserNotFoundException if the requested user
     *         does not exist in the local server.
     */
    public void enableUser(String username) throws UserNotFoundException
    {
        User user = getUser(username);
        LockOutManager.getInstance().enableAccount(username);
    }
    
    public void updateUser(String username, String password, String name, String email, String groupNames, int sortNumber)
            throws UserNotFoundException
    {
        User user = getUser(username);
        if (password != null) user.setPassword(password);
        if (name != null) user.setName(name);
        if (email != null) user.setEmail(email);

        if (groupNames != null) {
            Collection<Group> newGroups = new ArrayList<Group>();
            StringTokenizer tkn = new StringTokenizer(groupNames, ",");
            while (tkn.hasMoreTokens()) {
                newGroups.add(this.getOrCreateGroup(tkn.nextToken()));
            }

            Collection<Group> existingGroups = GroupManager.getInstance().getGroups(user);// 用户存在的组
            // Get the list of groups to add to the user
            Collection<Group> groupsToAdd =  new ArrayList<Group>(newGroups); // 用户新进的组
            groupsToAdd.removeAll(existingGroups); // 移除已经存在的组
            // Get the list of groups to remove from the user
            Collection<Group> groupsToDelete =  new ArrayList<Group>(existingGroups); 
            groupsToDelete.removeAll(newGroups);

            for (Group group : existingGroups) { // 更新用户所在的分组的排序号
            	groupProvider.updateMemberSortNumber(group.getName(), server.createJID(username, null), sortNumber);
            }
            
            // Add the user to the new groups
            for (Group group : groupsToAdd) {
                group.getMembers().add(server.createJID(username, null));
                groupProvider.updateMemberSortNumber(group.getName(), server.createJID(username, null), sortNumber);
            }
            // Remove the user from the old groups
            for (Group group : groupsToDelete) {
                group.getMembers().remove(server.createJID(username, null));
            }
        }
    }
    
    public void clearGroupCache(){
    }
    
    /**
     * Returns the the requested user or <tt>null</tt> if there are any
     * problems that don't throw an error.
     *
     * @param username the username of the local user to retrieve.
     * @return the requested user.
     * @throws UserNotFoundException if the requested user
     *         does not exist in the local server.
     */
    private User getUser(String username) throws UserNotFoundException {
        JID targetJID = server.createJID(username, null);
        // Check that the sender is not requesting information of a remote server entity
        if (targetJID.getNode() == null) {
            // Sender is requesting presence information of an anonymous user
            throw new UserNotFoundException("Username is null");
        }
        return userManager.getUser(targetJID.getNode());
    }
    
    /**
     * Returns the secret key that only valid requests should know.
     *
     * @return the secret key.
     */
    public String getSecret() {
        return secret;
    }

    /**
     * Sets the secret key that grants permission to use the userservice.
     *
     * @param secret the secret key.
     */
    public void setSecret(String secret) {
        JiveGlobals.setProperty("plugin.userservice.secret", secret);
        this.secret = secret;
    }

    public Collection<String> getAllowedIPs() {
        return allowedIPs;
    }

    public void setAllowedIPs(Collection<String> allowedIPs) {
        JiveGlobals.setProperty("plugin.userservice.allowedIPs", StringUtils.collectionToString(allowedIPs));
        this.allowedIPs = allowedIPs;
    }

    /**
     * Returns true if the user service is enabled. If not enabled, it will not accept
     * requests to create new accounts.
     *
     * @return true if the user service is enabled.
     */
    public boolean isEnabled() {
        return enabled;
    }

    /**
     * Enables or disables the user service. If not enabled, it will not accept
     * requests to create new accounts.
     *
     * @param enabled true if the user service should be enabled.
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
        JiveGlobals.setProperty("plugin.userservice.enabled",  enabled ? "true" : "false");
    }

    @Override
	public void propertySet(String property, Map<String, Object> params) {
        if (property.equals("plugin.userservice.secret")) {
            this.secret = (String)params.get("value");
        }
        else if (property.equals("plugin.userservice.enabled")) {
            this.enabled = Boolean.parseBoolean((String)params.get("value"));
        }
        else if (property.equals("plugin.userservice.allowedIPs")) {
            this.allowedIPs = StringUtils.stringToCollection((String)params.get("value"));
        }
    }

    @Override
	public void propertyDeleted(String property, Map<String, Object> params) {
        if (property.equals("plugin.userservice.secret")) {
            this.secret = "";
        }
        else if (property.equals("plugin.userservice.enabled")) {
            this.enabled = false;
        }
        else if (property.equals("plugin.userservice.allowedIPs")) {
            this.allowedIPs = Collections.emptyList();
        }
    }

    @Override
	public void xmlPropertySet(String property, Map<String, Object> params) {
        // Do nothing
    }

    @Override
	public void xmlPropertyDeleted(String property, Map<String, Object> params) {
        // Do nothing
    }
    
    public static void main(String[] args) {
//		AuthCheckFilter.addExclude("http://127.0.0.1:9090/plugins/kingrayplugin/pages/userList.jsp");
		System.out.println(AuthCheckFilter.testURLPassesExclude("/plugins/kingrayplugin/pages/userList.jsp", "/kingrayplugin/pages/*"));
		try {
			Class.forName("com.kingray.plugin.user.CryptUserProvider");
			Class.forName("com.kingray.plugin.auth.CryptAuthProvider");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
    }

	public void updateGroupSortNumber(String groupName, int sortNumber) {
		groupProvider.updateGroupSortNumber(groupName, sortNumber);
	}
}
